# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
LowLevelContextMenu.py

This application was an experiment to figure out the sequence of Windows API
calls required to draw a context menu for a notification icon.  It does not
use the high-level winnotify NotifyIcon class, but rather makes the C API calls
directly.  It's a bit of a mess and not terribly elegant, but this research will
influence the design of NotifyIcon.
"""

from __future__ import print_function
from __future__ import unicode_literals

import ctypes
import winnotify.winapi as winapi

from winnotify.winapi_constants import CW_USEDEFAULT
from winnotify.winapi_constants import MF_ENABLED
from winnotify.winapi_constants import MF_STRING
from winnotify.winapi_constants import NIF_GUID
from winnotify.winapi_constants import NIF_ICON
from winnotify.winapi_constants import NIF_MESSAGE
from winnotify.winapi_constants import NIF_SHOWTIP
from winnotify.winapi_constants import NIF_TIP
from winnotify.winapi_constants import NIM_ADD
from winnotify.winapi_constants import NIM_DELETE
from winnotify.winapi_constants import NIM_SETVERSION
from winnotify.winapi_constants import NOTIFYICON_VERSION_4
from winnotify.winapi_constants import SHGSI_ICON
from winnotify.winapi_constants import TPM_BOTTOMALIGN
from winnotify.winapi_constants import TPM_CENTERALIGN
from winnotify.winapi_constants import WM_USER

MSG_TESTCLASS = WM_USER + 100
NINF_KEY = 0x1
NIN_SELECT = WM_USER + 0

def MyTestClassWndProc_py(hwnd, message, wparam, lparam):
    try:
        msg_name = WINDOW_MESSAGES[message]
    except KeyError:
        msg_name = "unknown message"
    print("MyTestClassWndProc_py({!r}, {}, {!r}, {!r})".format(hwnd, msg_name, wparam, lparam))

    if msg_name == "WM_NCCREATE":
        return 1
    elif msg_name == "MSG_TESTCLASS":
        notification_event = lparam & 0x0000ffff
        try:
            notification_event_name = WINDOW_MESSAGES[notification_event]
        except KeyError:
            notification_event_name = "unknown message"
        icon_id = (lparam & 0xffff0000) >> 16
        print("MSG_TESTCLASS notification_event={} icon_id={}".format(notification_event_name, icon_id))
        if notification_event_name != "WM_CONTEXTMENU":
            return 0
        else:
            xpos = wparam & 0x0000ffff
            ypos = (wparam & 0xffff0000) >> 16
            print("WM_CONTEXTMENU x={} y={}".format(xpos, ypos))

            hMenu = winapi.CreatePopupMenu()
            if hMenu is None:
                raise ctypes.WinError()

            uIDNewItem = ctypes.wintypes.UINT()
            result = winapi.AppendMenu(
                hMenu=hMenu,
                uFlags=MF_ENABLED|MF_STRING,
                uIDNewItem=ctypes.pointer(uIDNewItem),
                lpNewItem="Exit",
            )
            if result == 0:
                raise ctypes.WinError()

            result = winapi.TrackPopupMenuEx(
                hmenu=hMenu,
                fuFlags=TPM_CENTERALIGN|TPM_BOTTOMALIGN,
                x=xpos,
                y=ypos,
                hwnd=hwnd,
                lptpm=None,
            )
            if result == 0:
                raise ctypes.WinError()

            return 1

    elif msg_name == "WM_MENUSELECT":
        item_index = wparam & 0xffff
        print("Menu item selected: {}".format(item_index))
        if item_index == 0:
            winapi.PostQuitMessage()

    result = winapi.DefWindowProc(
        hWnd=hwnd,
        Msg=message,
        wParam=wparam,
        lParam=lparam,
    )
    return result

def main():
    hInstance = winapi.GetModuleHandle()

    wndClass = winapi.WNDCLASSEX()
    wndClass.cbSize = winapi.WNDCLASSEX_SIZE
    wndClass.lpfnWndProc = winapi.WNDPROC(MyTestClassWndProc_py)
    wndClass.hInstance = hInstance
    wndClass.lpszClassName = "MyTestClass"

    print("Registering window class {}".format(wndClass.lpszClassName))
    class_atom = winapi.RegisterClassEx(wndClass)
    if class_atom == 0:
        raise ctypes.WinError()

    print("Creating window")
    window_handle = winapi.CreateWindowEx(
        dwExStyle=0,
        lpClassName=winapi.MAKEINTRESOURCE(class_atom),
        lpWindowName="Denver's Window",
        dwStyle=0,
        X=CW_USEDEFAULT,
        Y=CW_USEDEFAULT,
        nWidth=CW_USEDEFAULT,
        nHeight=CW_USEDEFAULT,
        hInstance=hInstance,
    )
    if not window_handle:
        raise ctypes.WinError()
    print("Created window {}".format(window_handle))

    print("Getting stock icon")
    icon_info = winapi.SHSTOCKICONINFO()
    icon_info.cbSize = winapi.SHSTOCKICONINFO_SIZE
    hresult = winapi.SHGetStockIconInfo(
        siid=4,
        uFlags=SHGSI_ICON,
        psii=icon_info,
    )
    if hresult != 0:
        raise Exception("unable to load shell icon: "
            "SHGetStockIconInfo failed: {}".format(hresult))
    hIcon = icon_info.hIcon

    guidItem = winapi.GUID()
    guidItem.from_str(b"abcd" * 4)

    nid = winapi.NOTIFYICONDATA()
    nid.cbSize = winapi.NOTIFYICONDATA_SIZE
    nid.hWnd = window_handle
    nid.uFlags = NIF_ICON | NIF_TIP | NIF_GUID | NIF_SHOWTIP | NIF_MESSAGE
    nid.uCallbackMessage = MSG_TESTCLASS
    nid.hIcon = hIcon
    nid.szTip = "This is Denver's icon"
    nid.uVersion = NOTIFYICON_VERSION_4
    nid.guidItem = guidItem

    print("Registering notification icon")
    result = winapi.Shell_NotifyIcon(NIM_ADD, nid)
    if not result:
        raise Exception("Shell_NotifyIcon(NIM_ADD, nid) failed")
    winapi.Shell_NotifyIcon(NIM_SETVERSION, nid)
    if not result:
        raise Exception("Shell_NotifyIcon(NIM_SETVERSION, nid) failed")

    print("Starting message loop")
    msg = winapi.MSG()
    while True:
        get_message_result = winapi.GetMessage(msg, window_handle, 0, 0)
        if get_message_result == 0:
            break # end of message loop
        elif get_message_result == -1:
            raise ctypes.WinError()
        else:
            winapi.DispatchMessage(msg)

    print("Unregistering notification icon")
    result = winapi.Shell_NotifyIcon(NIM_DELETE, nid)

WINDOW_MESSAGES = {
    0x0000: "WM_NULL",
    0x0001: "WM_CREATE",
    0x0002: "WM_DESTROY",
    0x0003: "WM_MOVE",
    0x0005: "WM_SIZE",
    0x0006: "WM_ACTIVATE",
    0x0007: "WM_SETFOCUS",
    0x0008: "WM_KILLFOCUS",
    0x000A: "WM_ENABLE",
    0x000B: "WM_SETREDRAW",
    0x000C: "WM_SETTEXT",
    0x000D: "WM_GETTEXT",
    0x000E: "WM_GETTEXTLENGTH",
    0x000F: "WM_PAINT",
    0x0010: "WM_CLOSE",
    0x0011: "WM_QUERYENDSESSION",
    0x0013: "WM_QUERYOPEN",
    0x0016: "WM_ENDSESSION",
    0x0012: "WM_QUIT",
    0x0014: "WM_ERASEBKGND",
    0x0015: "WM_SYSCOLORCHANGE",
    0x0018: "WM_SHOWWINDOW",
    0x001A: "WM_WININICHANGE",
    0x001A: "WM_SETTINGCHANGE",
    0x001B: "WM_DEVMODECHANGE",
    0x001C: "WM_ACTIVATEAPP",
    0x001D: "WM_FONTCHANGE",
    0x001E: "WM_TIMECHANGE",
    0x001F: "WM_CANCELMODE",
    0x0020: "WM_SETCURSOR",
    0x0021: "WM_MOUSEACTIVATE",
    0x0022: "WM_CHILDACTIVATE",
    0x0023: "WM_QUEUESYNC",
    0x0024: "WM_GETMINMAXINFO",
    0x0026: "WM_PAINTICON",
    0x0027: "WM_ICONERASEBKGND",
    0x0028: "WM_NEXTDLGCTL",
    0x002A: "WM_SPOOLERSTATUS",
    0x002B: "WM_DRAWITEM",
    0x002C: "WM_MEASUREITEM",
    0x002D: "WM_DELETEITEM",
    0x002E: "WM_VKEYTOITEM",
    0x002F: "WM_CHARTOITEM",
    0x0030: "WM_SETFONT",
    0x0031: "WM_GETFONT",
    0x0032: "WM_SETHOTKEY",
    0x0033: "WM_GETHOTKEY",
    0x0037: "WM_QUERYDRAGICON",
    0x0039: "WM_COMPAREITEM",
    0x003D: "WM_GETOBJECT",
    0x0041: "WM_COMPACTING",
    0x0044: "WM_COMMNOTIFY",
    0x0046: "WM_WINDOWPOSCHANGING",
    0x0047: "WM_WINDOWPOSCHANGED",
    0x0048: "WM_POWER",
    0x004A: "WM_COPYDATA",
    0x004B: "WM_CANCELJOURNAL",
    0x004E: "WM_NOTIFY",
    0x0050: "WM_INPUTLANGCHANGEREQUEST",
    0x0051: "WM_INPUTLANGCHANGE",
    0x0052: "WM_TCARD",
    0x0053: "WM_HELP",
    0x0054: "WM_USERCHANGED",
    0x0055: "WM_NOTIFYFORMAT",
    0x007B: "WM_CONTEXTMENU",
    0x007C: "WM_STYLECHANGING",
    0x007D: "WM_STYLECHANGED",
    0x007E: "WM_DISPLAYCHANGE",
    0x007F: "WM_GETICON",
    0x0080: "WM_SETICON",
    0x0081: "WM_NCCREATE",
    0x0082: "WM_NCDESTROY",
    0x0083: "WM_NCCALCSIZE",
    0x0084: "WM_NCHITTEST",
    0x0085: "WM_NCPAINT",
    0x0086: "WM_NCACTIVATE",
    0x0087: "WM_GETDLGCODE",
    0x0088: "WM_SYNCPAINT",
    0x00A0: "WM_NCMOUSEMOVE",
    0x00A1: "WM_NCLBUTTONDOWN",
    0x00A2: "WM_NCLBUTTONUP",
    0x00A3: "WM_NCLBUTTONDBLCLK",
    0x00A4: "WM_NCRBUTTONDOWN",
    0x00A5: "WM_NCRBUTTONUP",
    0x00A6: "WM_NCRBUTTONDBLCLK",
    0x00A7: "WM_NCMBUTTONDOWN",
    0x00A8: "WM_NCMBUTTONUP",
    0x00A9: "WM_NCMBUTTONDBLCLK",
    0x00AB: "WM_NCXBUTTONDOWN",
    0x00AC: "WM_NCXBUTTONUP",
    0x00AD: "WM_NCXBUTTONDBLCLK",
    0x00FE: "WM_INPUT_DEVICE_CHANGE",
    0x00FF: "WM_INPUT",
    0x0100: "WM_KEYFIRST",
    0x0100: "WM_KEYDOWN",
    0x0101: "WM_KEYUP",
    0x0102: "WM_CHAR",
    0x0103: "WM_DEADCHAR",
    0x0104: "WM_SYSKEYDOWN",
    0x0105: "WM_SYSKEYUP",
    0x0106: "WM_SYSCHAR",
    0x0107: "WM_SYSDEADCHAR",
    0x0109: "WM_UNICHAR",
    0x0109: "WM_KEYLAST",
    0x0108: "WM_KEYLAST",
    0x010D: "WM_IME_STARTCOMPOSITION",
    0x010E: "WM_IME_ENDCOMPOSITION",
    0x010F: "WM_IME_COMPOSITION",
    0x010F: "WM_IME_KEYLAST",
    0x0110: "WM_INITDIALOG",
    0x0111: "WM_COMMAND",
    0x0112: "WM_SYSCOMMAND",
    0x0113: "WM_TIMER",
    0x0114: "WM_HSCROLL",
    0x0115: "WM_VSCROLL",
    0x0116: "WM_INITMENU",
    0x0117: "WM_INITMENUPOPUP",
    0x0119: "WM_GESTURE",
    0x011A: "WM_GESTURENOTIFY",
    0x011F: "WM_MENUSELECT",
    0x0120: "WM_MENUCHAR",
    0x0121: "WM_ENTERIDLE",
    0x0122: "WM_MENURBUTTONUP",
    0x0123: "WM_MENUDRAG",
    0x0124: "WM_MENUGETOBJECT",
    0x0125: "WM_UNINITMENUPOPUP",
    0x0126: "WM_MENUCOMMAND",
    0x0127: "WM_CHANGEUISTATE",
    0x0128: "WM_UPDATEUISTATE",
    0x0129: "WM_QUERYUISTATE",
    0x0132: "WM_CTLCOLORMSGBOX",
    0x0133: "WM_CTLCOLOREDIT",
    0x0134: "WM_CTLCOLORLISTBOX",
    0x0135: "WM_CTLCOLORBTN",
    0x0136: "WM_CTLCOLORDLG",
    0x0137: "WM_CTLCOLORSCROLLBAR",
    0x0138: "WM_CTLCOLORSTATIC",
    0x0200: "WM_MOUSEFIRST",
    0x0200: "WM_MOUSEMOVE",
    0x0201: "WM_LBUTTONDOWN",
    0x0202: "WM_LBUTTONUP",
    0x0203: "WM_LBUTTONDBLCLK",
    0x0204: "WM_RBUTTONDOWN",
    0x0205: "WM_RBUTTONUP",
    0x0206: "WM_RBUTTONDBLCLK",
    0x0207: "WM_MBUTTONDOWN",
    0x0208: "WM_MBUTTONUP",
    0x0209: "WM_MBUTTONDBLCLK",
    0x020A: "WM_MOUSEWHEEL",
    0x020B: "WM_XBUTTONDOWN",
    0x020C: "WM_XBUTTONUP",
    0x020D: "WM_XBUTTONDBLCLK",
    0x020E: "WM_MOUSEHWHEEL",
    0x020E: "WM_MOUSELAST",
    0x020D: "WM_MOUSELAST",
    0x020A: "WM_MOUSELAST",
    0x0209: "WM_MOUSELAST",
    0x0210: "WM_PARENTNOTIFY",
    0x0211: "WM_ENTERMENULOOP",
    0x0212: "WM_EXITMENULOOP",
    0x0213: "WM_NEXTMENU",
    0x0214: "WM_SIZING",
    0x0215: "WM_CAPTURECHANGED",
    0x0216: "WM_MOVING",
    0x0218: "WM_POWERBROADCAST",
    0x0219: "WM_DEVICECHANGE",
    0x0220: "WM_MDICREATE",
    0x0221: "WM_MDIDESTROY",
    0x0222: "WM_MDIACTIVATE",
    0x0223: "WM_MDIRESTORE",
    0x0224: "WM_MDINEXT",
    0x0225: "WM_MDIMAXIMIZE",
    0x0226: "WM_MDITILE",
    0x0227: "WM_MDICASCADE",
    0x0228: "WM_MDIICONARRANGE",
    0x0229: "WM_MDIGETACTIVE",
    0x0230: "WM_MDISETMENU",
    0x0231: "WM_ENTERSIZEMOVE",
    0x0232: "WM_EXITSIZEMOVE",
    0x0233: "WM_DROPFILES",
    0x0234: "WM_MDIREFRESHMENU",
    0x0240: "WM_TOUCH",
    0x0281: "WM_IME_SETCONTEXT",
    0x0282: "WM_IME_NOTIFY",
    0x0283: "WM_IME_CONTROL",
    0x0284: "WM_IME_COMPOSITIONFULL",
    0x0285: "WM_IME_SELECT",
    0x0286: "WM_IME_CHAR",
    0x0288: "WM_IME_REQUEST",
    0x0290: "WM_IME_KEYDOWN",
    0x0291: "WM_IME_KEYUP",
    0x02A1: "WM_MOUSEHOVER",
    0x02A3: "WM_MOUSELEAVE",
    0x02A0: "WM_NCMOUSEHOVER",
    0x02A2: "WM_NCMOUSELEAVE",
    0x02B1: "WM_WTSSESSION_CHANGE",
    0x02c0: "WM_TABLET_FIRST",
    0x02df: "WM_TABLET_LAST",
    0x0300: "WM_CUT",
    0x0301: "WM_COPY",
    0x0302: "WM_PASTE",
    0x0303: "WM_CLEAR",
    0x0304: "WM_UNDO",
    0x0305: "WM_RENDERFORMAT",
    0x0306: "WM_RENDERALLFORMATS",
    0x0307: "WM_DESTROYCLIPBOARD",
    0x0308: "WM_DRAWCLIPBOARD",
    0x0309: "WM_PAINTCLIPBOARD",
    0x030A: "WM_VSCROLLCLIPBOARD",
    0x030B: "WM_SIZECLIPBOARD",
    0x030C: "WM_ASKCBFORMATNAME",
    0x030D: "WM_CHANGECBCHAIN",
    0x030E: "WM_HSCROLLCLIPBOARD",
    0x030F: "WM_QUERYNEWPALETTE",
    0x0310: "WM_PALETTEISCHANGING",
    0x0311: "WM_PALETTECHANGED",
    0x0312: "WM_HOTKEY",
    0x0317: "WM_PRINT",
    0x0318: "WM_PRINTCLIENT",
    0x0319: "WM_APPCOMMAND",
    0x031A: "WM_THEMECHANGED",
    0x031D: "WM_CLIPBOARDUPDATE",
    0x031E: "WM_DWMCOMPOSITIONCHANGED",
    0x031F: "WM_DWMNCRENDERINGCHANGED",
    0x0320: "WM_DWMCOLORIZATIONCOLORCHANGED",
    0x0321: "WM_DWMWINDOWMAXIMIZEDCHANGE",
    0x0323: "WM_DWMSENDICONICTHUMBNAIL",
    0x0326: "WM_DWMSENDICONICLIVEPREVIEWBITMAP",
    0x033F: "WM_GETTITLEBARINFOEX",
    0x0358: "WM_HANDHELDFIRST",
    0x035F: "WM_HANDHELDLAST",
    0x0360: "WM_AFXFIRST",
    0x037F: "WM_AFXLAST",
    0x0380: "WM_PENWINFIRST",
    0x038F: "WM_PENWINLAST",
    0x8000: "WM_APP",
    0x0400: "WM_USER",
    MSG_TESTCLASS: "MSG_TESTCLASS",
    (WM_USER + 0): "NIN_SELECT",
    (NIN_SELECT | NINF_KEY): "NIN_KEYSELECT",
    (WM_USER + 2): "NIN_BALLOONSHOW",
    (WM_USER + 3): "NIN_BALLOONHIDE",
    (WM_USER + 4): "NIN_BALLOONTIMEOUT",
    (WM_USER + 5): "NIN_BALLOONUSERCLICK",
    (WM_USER + 6): "NIN_POPUPOPEN",
    (WM_USER + 7): "NIN_POPUPCLOSE",
}

if __name__ == "__main__":
    main()
